/**
 * 
 */
package ro.dta.idbi.patterns;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ro.dta.idbi.interfaces.IntegrationOperation;
import ro.dta.idbi.model.AbstractPattern;
import ro.dta.idbi.model.AutoMedUtils;
import ro.dta.idbi.model.Utils;
import uk.ac.ic.doc.automed.IntegrityException;
import uk.ac.ic.doc.automed.NotFoundException;
import uk.ac.ic.doc.automed.modelmanagement.modeldef.SQLModelDef;
import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.reps.SchemaObject;

/**
 * <b>Addition of intersection transformation pattern</b><br />
 * 
 * Example of use:<br />
 * <code>add_intersection (&lt;&lt;newTable&gt;&gt;, &lt;&lt;table1&gt;&gt;, &lt;&lt;table2&gt;&gt;, ..., &lt;&lt;tablen&gt;&gt;)</code>
 * <br />
 * 
 * <i>newTable</i> is created as a child of &lt;&lt;table1&gt;&gt;,
 * &lt;&lt;table2&gt;&gt;, ..., &lt;&lt;tablen&gt;&gt;
 * 
 * @author Tudor Dobrila
 * 
 */
public class AddIntersection extends AbstractPattern {

	/**
	 * Name of the pattern
	 */
	public static final String NAME = "Addition of Intersection";

	/**
	 * Description of the pattern
	 */
	public static final String DESCRIPTION = "Introduce a new table as a child of the existing tables";

	/**
	 * Name of the command associated with the pattern
	 */
	public static final String COMMAND = "add_intersection";

	/**
	 * Integration phase during which the command is applied
	 */
	public static final EnumSet<IntegrationOperation> OPERATION = EnumSet
			.of(IntegrationOperation.MERGING);

	/**
	 * Name of the newly created table that will be an intersection of the other
	 * tables
	 */
	private String newTableStr;

	/**
	 * List of parent tables of the newly created table
	 */
	private List<String> parentTbls;

	/**
	 * Addition of intersection pattern class constructor
	 * 
	 * @param sourceSchema
	 *            Source schema over which the transformation is applied
	 */
	public AddIntersection(final Schema sourceSchema) throws NotFoundException, IntegrityException {
		super(sourceSchema);

		pattern = Pattern.compile("^" + COMMAND + "[ ]*\\((" + TABLE_REGEX + ")" + COMMA_REGEX
				+ TABLE_SEQ_REGEX + "\\)$");
	}

	/**
	 * Addition of intersection pattern class constructor
	 * 
	 * @param sourceSchema
	 *            Name of source schema over which the transformation is applied
	 */
	public AddIntersection(final String sourceSchema) throws NotFoundException, IntegrityException {
		this(Schema.getSchema(sourceSchema));
	}

	@Override
	protected List<String> getPrimitiveCommands() throws NotFoundException, IntegrityException {
		List<String> result = new ArrayList<String>();

		final SchemaObject origTable = sourceSchema
				.getSchemaObject("<<" + parentTbls.get(0) + ">>");
		final List<String> pks = AutoMedUtils.getPK(sourceSchema, origTable);

		// Step 1: add the new table as an intersection of the other tables
		StringBuilder cmd = new StringBuilder("add (").append(
				Utils.genTableRepresentation(newTableStr)).append(", ");
		// Calculate extent of the table
		StringBuilder extent = new StringBuilder();
		for (String tbl : parentTbls) {
			extent.append("<<").append(tbl).append(">> intersect ");
		}
		extent.setLength(extent.length() - 11);
		cmd.append(extent).append(" )");
		result.add(cmd.toString());

		// Step 2: add the PK columns to the newly created table
		Object[] pkArr = new Object[2 + pks.size()];
		pkArr[0] = newTableStr + "_pk";
		pkArr[1] = newTableStr;
		int crtPos = 2;
		extent = new StringBuilder();
		for (String pk : pks) {
			cmd.setLength(0);
			cmd.append("add (");

			// Calculate extent of the column
			extent.setLength(0);
			for (String tbl : parentTbls) {
				extent.append("<<").append(tbl).append(",").append(pk).append(">> intersect ");
			}
			extent.setLength(extent.length() - 11);

			final SchemaObject crtColObj = sourceSchema.getSchemaObject("<<" + parentTbls.get(0)
					+ "," + pk + ">>");
			Object[] crtColDef = crtColObj.getSchemeDefinition();
			cmd.append(Utils.genColRepresentation(newTableStr, crtColDef[1], crtColDef[2],
					crtColDef[3]));
			cmd.append(",").append(extent).append(" )");
			result.add(cmd.toString());

			pkArr[crtPos++] = "<<" + newTableStr + "," + crtColDef[1] + ">>";
		}

		// Step 3: add the PK constraint to the newly created table
		if (sqlModel.isFeatureInUse(SQLModelDef.PRIMARY_KEY)) {
			cmd.setLength(0);
			cmd.append("add (").append(Utils.genPKRepresentation(pkArr)).append(")");
			result.add(cmd.toString());
		}

		// Step 4: add the FK constraints from the intersection table to the
		// parent tables
		if (sqlModel.isFeatureInUse(SQLModelDef.FOREIGN_KEY)) {
			for (String tbl : parentTbls) {
				cmd.setLength(0);
				cmd.append("add (").append(Utils.genFKRepresentation(newTableStr, tbl, pks))
						.append(")");
				result.add(cmd.toString());
			}

		}

		return result;
	}

	@Override
	protected boolean verify() {
		// TODO Auto-generated method stub
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.model.AbstractPattern#parse(java.lang.String)
	 */
	@Override
	protected void parse(final String trans) {
		final Matcher matcher = pattern.matcher(trans);
		matcher.find();

		newTableStr = parseTableName(matcher.group(1));
		final List<String> tbl = splitValues(matcher.group(2));

		parentTbls = new ArrayList<String>(tbl.size());
		for (String t : tbl) {
			parentTbls.add(parseTableName(t));
		}
	}

	/**
	 * Set the name of the newly created table that will be an intersection of
	 * the other tables
	 * 
	 * @param newTableStr
	 *            New name of the newly created table that will be an
	 *            intersection of the other tables
	 */
	public void setNewTableStr(final String newTableStr) {
		this.newTableStr = newTableStr;
	}

	/**
	 * @return Name of the newly created table that will be an intersection of
	 *         the other tables
	 */
	public String getNewTableStr() {
		return newTableStr;
	}

	/**
	 * @param parentTbls
	 *            New list of parent tables of the newly created table
	 */
	public void setParentTbls(final List<String> parentTbls) {
		this.parentTbls = parentTbls;
	}

	/**
	 * @return List of parent tables of the newly created table
	 */
	public List<String> getParentTbls() {
		return parentTbls;
	}

}
